/*	
*	File: rip-header.h
*	Date: 2011/12/06
*	User: Ronmaru
*	Desctiption: RIPv2 protocol header
*/

#ifndef _RIP_HEADER_H_
#define _RIP_HEADER_H_

//  RIPv2 protocol header
//
//   0               1               2               3      
//   0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
//   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//   |  command (1)  |  version (1)  |       must be zero (2)        |
//   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//   | Address Family Identifier (2) |        Route Tag (2)          |
//   +-------------------------------+-------------------------------+
//   |                         IP Address (4)                        |
//   +---------------------------------------------------------------+
//   |                         Subnet Mask (4)                       |
//   +---------------------------------------------------------------+
//   |                         Next Hop (4)                          |
//   +---------------------------------------------------------------+
//   |                         Metric (4)                            |
//   +---------------------------------------------------------------+


const int RIP_MAX_HEADER_LENGTH = 24;

class RIPheader
{
public:
  RIPheader() { std::fill(rep_, rep_ + sizeof(rep_), 0); }

  void SetCommand(unsigned short n) { rep_[0] = n; }
  void SetVersion() { rep_[1] = 2; }
  void SetRoutingDomain(unsigned int n) { Encode(2, 3, n); }
  void SetAFIdent(unsigned short n) { Encode(4, 5, n); } //allways equal 2
  void SetRouteTag(unsigned short n) { Encode(6, 7, n); }
  void SetIPAddr(boost::asio::ip::address_v4 address) {
    boost::asio::ip::address_v4::bytes_type bytes = address.to_bytes();
    rep_[8] = bytes[0];
    rep_[9] = bytes[1];
    rep_[10] = bytes[2];
    rep_[11] = bytes[3];
  }
  void SetMask(boost::asio::ip::address_v4 mask) {
    boost::asio::ip::address_v4::bytes_type bytes = mask.to_bytes();
    rep_[12] = bytes[0];
    rep_[13] = bytes[1];
    rep_[14] = bytes[2];
    rep_[15] = bytes[3];
  }
  void SetNextHop(boost::asio::ip::address_v4 hop) {
    boost::asio::ip::address_v4::bytes_type bytes = hop.to_bytes();
    rep_[16] = bytes[0];
    rep_[17] = bytes[1];
    rep_[18] = bytes[2];
    rep_[19] = bytes[3];
  }
  void SetMetric(unsigned long n) { Encode(20, 21, 22, 23, n); }

  unsigned short GetCommand() const { return rep_[0]; }
  unsigned short GetAFIdent() const { return Decode(4, 5); }
  unsigned short GetRouteTag() const { return Decode(6, 7); }
  boost::asio::ip::address_v4 GetIPAddr() const {
    boost::asio::ip::address_v4::bytes_type bytes
      = { { rep_[8], rep_[9], rep_[10], rep_[11] } };
    return boost::asio::ip::address_v4(bytes);
  }
  boost::asio::ip::address_v4 GetMask() const {
    boost::asio::ip::address_v4::bytes_type bytes
      = { { rep_[12], rep_[13], rep_[14], rep_[15] } };
    return boost::asio::ip::address_v4(bytes);
  }
  boost::asio::ip::address_v4 GetNextHop() const {
    boost::asio::ip::address_v4::bytes_type bytes
      = { { rep_[16], rep_[17], rep_[18], rep_[19] } };
    return boost::asio::ip::address_v4(bytes);
  }
  unsigned long GetMetric() const { return Decode(20, 21, 22, 23); }

  friend std::istream& operator>>(std::istream& is, RIPheader& header)
  { return is.read(reinterpret_cast<char*>(header.rep_), RIP_MAX_HEADER_LENGTH); }

  friend std::ostream& operator<<(std::ostream& os, const RIPheader& header)
  { return os.write(reinterpret_cast<const char*>(header.rep_), RIP_MAX_HEADER_LENGTH); }

private:
  unsigned short Decode(int a, int b) const
  { return (rep_[a] << 8) + rep_[b]; }
  unsigned long Decode(int a, int b, int c, int d) const
  { return (rep_[a] << 24) + (rep_[b] << 16) + (rep_[c] << 8) + rep_[d]; }

  void Encode(int a, int b, unsigned short n)
  {
    rep_[a] = static_cast<unsigned char>(n >> 8);
    rep_[b] = static_cast<unsigned char>(n & 0xFF);
  }
  void Encode(int a, int b, int c, int d, unsigned long n)
  {
    rep_[a] = static_cast<unsigned char>(n >> 24);
    rep_[b] = static_cast<unsigned char>((n >> 16) & 0xFF);
    rep_[c] = static_cast<unsigned char>((n >> 8) & 0xFF);
    rep_[d] = static_cast<unsigned char>(n & 0xFF);
  }

  unsigned char rep_[RIP_MAX_HEADER_LENGTH];
};

#endif //_RIP_HEADER_H_